using System;
using System.Collections.Generic;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Caching;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using OpenFlashChartLib;
using System.Text;

[assembly: WebResource(OpenFlashChart.OpenFlashChart_SwfObjectScript, "text/javascript")]

namespace OpenFlashChartLib
{
    public class OpenFlashChart : Control
    {
        public const string OpenFlashChart_SwfObjectScript = "OpenFlashChartLib.Resources.swfobject.js";
        /// <summary>
        /// Handle this event to create graph object inside the page
        /// </summary>
        public event DrawChartEventHandler DrawChart;
        /// <summary>
        /// Handle this event to register client script to use with AJAX Script Manager
        /// </summary>
        public event RegisterScriptEventHandler ScriptRegister;

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            int random = DateTime.Now.Millisecond;
            StringBuilder script = new StringBuilder();
            if (EmbedScriptType == EmbedScriptType.JQuery)
            {
                script.AppendFormat(
                    "var f{0} = {1};\n$('#{2}').flash({{src:f{0}.FlashFileUrl, width: f{0}.Width, height:f{0}.Height, flashvars:f{0}.flashv}});",
                    random, GetJQueryRegisterScript(), ClientID);

                if (Page.Request.Browser.Browser.Contains("IE"))
                {
                    // Add error handler.
                    // The jquery-flash embeder does not work properly
                    script.Append("window.onerror=function(){return true;};");
                }
            }
            else
            {
                if (RegisterScript)
                {
                    Page.ClientScript.RegisterClientScriptResource(GetType(), OpenFlashChart_SwfObjectScript);
                }
                script.Append(BuildSwfRegisterScript());
            }
            RegisterStarupScript(script.ToString());
        }

        protected virtual void RegisterStarupScript(string scriptBody)
        {
            Page.ClientScript.RegisterStartupScript(GetType(), "embed", scriptBody, true);
            if (ScriptRegister != null)
            {
                ScriptRegister(this, new RegisterScriptEventArgs(scriptBody));
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            writer.Write("<div id=\"");
            writer.Write(ClientID);
            writer.Write("\"></div>");
        }

        protected void OnDrawChart(DrawChartEventArgs e)
        {
            if (DrawChart != null)
                DrawChart(this, e);
        }

        protected FlashParams BuildFlashParams()
        {
            FlashParams fl = new FlashParams();
            string dataUrl = BuildDataUrl();
            fl.FlashFileUrl = Page.ResolveUrl(BuildFlashUrl());
            fl.Variables.Add(new KeyValuePair<string, string>("data", dataUrl));
            fl.Height = Height;
            fl.Variables.Add(new KeyValuePair<string, string>("height", fl.Height.ToString()));
            fl.Width = Width;
            fl.Variables.Add(new KeyValuePair<string, string>("width", fl.Width.ToString()));
            return fl;
        }


        private string BuildSwfRegisterScript()
        {
            FlashParams fl = BuildFlashParams();
            StringBuilder sb = new StringBuilder();
            sb.Append("var so=new  SWFObject(\"");
            sb.Append(fl.FlashFileUrl);
            sb.Append("\", \"openflash\", \"");
            sb.Append(fl.Width);
            sb.Append("\", \"");
            sb.Append(fl.Height);
            sb.Append("\", \"8\", \"#FFFFFF\");\n");
            foreach (KeyValuePair<string, string> pair in fl.Variables)
            {
                sb.Append("so.addVariable(\"");
                sb.Append(pair.Key);
                sb.Append("\", \"");
                sb.Append(pair.Value);
                sb.Append("\");\n");
            }
            sb.Append("so.write(\"");
            sb.Append(ClientID);
            sb.Append("\");");
            return sb.ToString();
        }

        protected string GetJQueryRegisterScript()
        {
            FlashParams fl = BuildFlashParams();
            return Serialize(fl);
        }

        private string Serialize(FlashParams fl)
        {
            // serialize flash params to json object
            StringBuilder sb = new StringBuilder();
            sb.Append("{");
            WriteParam(sb, "FlashFileUrl", fl.FlashFileUrl, true);
            WriteParam(sb, "Width", fl.Width.ToString(), true);
            WriteParam(sb, "Height", fl.Height.ToString(), fl.Variables.Count > 0);
            if (fl.Variables.Count > 0)
            {
                // Name additional params as "flashv"
                sb.Append("\"flashv\":{");
                int i = 0;
                foreach (KeyValuePair<string, string> pair in fl.Variables)
                {
                    bool addComa = ++i < fl.Variables.Count;
                    WriteParam(sb, pair.Key, pair.Value, addComa);
                }
                sb.Append("}");
            }
            sb.Append("}");
            return sb.ToString();
        }

        private static void WriteParam(StringBuilder sb, string paramName, string paramValue, bool addComa)
        {
            sb.Append("\"");
            sb.Append(paramName);
            sb.Append("\":\"");
            sb.Append(paramValue);
            sb.Append("\"");
            if (addComa)
                sb.Append(",");
        }

        private string BuildFlashUrl()
        {
            if (FlashUrl == null)
            {
                return OpenFlashSettings.FlashFileUrl;
            }
            return null;
        }

        private string BuildDataUrl()
        {
            if (!string.IsNullOrEmpty(SpecificDataHandler))
            {
                return Page.ResolveUrl(SpecificDataHandler);
            }
            return string.Format("{0}?{1}={2}",
                Page.ResolveUrl(DataHandlerUrl), OpenFlashSettings.HandlerGetControlParam, UniqControlID);
        }

        private string _uniqControlID;
        private string UniqControlID
        {
            get
            {
                if (_uniqControlID != null)
                    return _uniqControlID;
                else
                {
                    _uniqControlID = Guid.NewGuid().ToString();
                    return _uniqControlID;
                }
            }
        }

        /// <summary>
        /// Gets the flash params object. You can add or remove 
        /// flash register params from it
        /// </summary>
        private FlashParams _flashRegisterParams = new FlashParams();
        public FlashParams FlashRegisterParams
        {
            get
            {
                object viewStObj = ViewState["RegParams"];
                if (viewStObj != null)
                {
                    return (FlashParams)viewStObj;
                }
                else
                {
                    return (FlashParams)(ViewState["RegParams"] = new FlashParams());
                }
            }
        }

        /// <summary>
        /// Gets or sets url where the swf file of open flash chart located
        /// </summary>
        public string FlashUrl
        {
            get { return FlashRegisterParams.FlashFileUrl; }
            set { FlashRegisterParams.FlashFileUrl = value; }
        }

        /// <summary>
        /// Gets or set open flash chart width
        /// </summary>
        public int Width
        {
            get { return FlashRegisterParams.Width; }
            set { FlashRegisterParams.Width = value; }
        }

        /// <summary>
        /// Gets or sets open flash chart height
        /// </summary>
        public int Height
        {
            get { return FlashRegisterParams.Height; }
            set { FlashRegisterParams.Height = value; }
        }

        /// <summary>
        /// Gets or sets url to data http handler wich you have to register in web.config
        /// </summary>
        public string DataHandlerUrl
        {
            get
            {
                object viewStObje = ViewState["dhurl"];
                if (viewStObje == null)
                {
                    return OpenFlashSettings.DataHandlerUrl;
                }
                return (string) viewStObje;
            }
            set
            {
                ViewState["dhurl"] = value;
            }
        }

        public EmbedScriptType EmbedScriptType
        {
            get
            {
                object viewStObj = ViewState["embType"];
                if (viewStObj == null)
                {
                    return EmbedScriptType.SwfObject;
                }
                return (EmbedScriptType) viewStObj;
            }
            set
            {
                ViewState["embType"] = value;
            }
        }

        public bool RegisterScript
        {
            get
            {
                object viewStObj = ViewState["regScr"];
                if (viewStObj == null)
                {
                    return true;
                }
                return (bool)viewStObj;
            }
            set
            {
                ViewState["regScr"] = value;
            }
        }

        /// <summary>
        /// Set this property to your http handler or page url which renders Graph object for current chart
        /// Do not set anything if you use DrawChart event to create Graph object
        /// </summary>
        public string SpecificDataHandler
        {
            get
            {
                object viewStObj = ViewState["specHanler"];
                if (viewStObj == null)
                {
                    return null;
                }
                return (string)viewStObj;
            }
            set
            {
                ViewState["specHanler"] = value;
            }
        }

        /// <summary>
        /// If you call this method DrawChart event fires to rebuild chart data
        /// The chart will display anything untill you call this method
        /// </summary>
        public virtual void BuildChart()
        {
            DrawChartEventArgs ee = new DrawChartEventArgs();
            OnDrawChart(ee);
            if (ee.Graph != null)
            {
                Page.Cache.Add(UniqControlID, ee.Graph, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, 10, 0),
                    CacheItemPriority.NotRemovable, null);
            }
            else
            {
                Page.Cache.Remove(UniqControlID);
            }

        }

    }

    public enum EmbedScriptType
    {
        SwfObject,
        JQuery
    }
}
